﻿using System.Collections.Generic;
using System;
using System.Runtime.InteropServices;

namespace Com.ZX
{
    public interface IObjectPool<T>
    {
        void RecyleObj(int id, Action<T> Func = null);
    }
    public delegate void RefAction<T>(ref T arg1);
    public class ObjectPool<T> : IObjectPool<T> where T : class, IPoolable<T>, new()
    {
        //private int m_nextAvailableIndex = 0;
        /// <summary>
        /// 初始下标
        /// </summary>
        private int _startCapacityIndex;
        public int startCapacityIndex { get { return this._startCapacityIndex; } }
        /// <summary>
        /// 可用下标
        /// </summary>
        private Queue<int> _avaliableIdList;

        /// <summary>
        /// 池中全部元素
        /// </summary>
        private Dictionary<int, T> _totalObjList;

        private Action<T> m_resetAction;
        private RefAction<T> m_clearAction;
        private Func<T, ObjectPool<T>, T> m_onetimeInitFunc;

        public int PoolId { get; set; }

        //private static object asyncLockIdList = new object();
        //private static object asyncLockObjList = new object();

        /// <summary>
        /// 初始化一个对象池
        /// </summary>
        /// <param name="initialBufferSize">初始数量</param>
        /// <param name="ResetAction">对象重利用回调</param>
        /// <param name="OnetimeInitAction">对象初始回调</param>
        public ObjectPool(int initialBufferSize,
            RefAction<T> ClearAction,
            Func<T, ObjectPool<T>, T> OnetimeInitFunc = null,
            Action<T> ResetAction = null)
        {
            _avaliableIdList = new Queue<int>(initialBufferSize);
            _totalObjList = new Dictionary<int, T>(initialBufferSize);
            _startCapacityIndex = 0;
            //m_objectList = new List<T>(initialBufferSize);
            m_resetAction = ResetAction;
            m_onetimeInitFunc = OnetimeInitFunc;
            m_clearAction = ClearAction;
            for (int i = 0; i < initialBufferSize; i++)
                this.NewObj();
        }
        public ObjectPool()
        { }

        ~ObjectPool()
        {
            this.Clear();
        }
        public int Count { get { return this._totalObjList.Count; } }

        //int i = 0;
        /// <summary>
        /// 取得一个物体，返回值 1,obj代表ID是1的物体被取到，ID可以用来归还物体的时候用到
        /// </summary>
        /// <returns></returns> 
        public T PickObj()
        {
            if (_avaliableIdList.Count == 0)
                this.NewObj();
            int id = 0;
            //lock (asyncLockIdList)
            {
                id = _avaliableIdList.Dequeue();
            }
            if (m_resetAction != null)
            {
                //lock (asyncLockObjList)
                {
                    m_resetAction((T)this._totalObjList[id]);
                }
            }

            return _totalObjList[id];

        }

        /// <summary>
        /// 从池中取出元素，在指定时间后回收
        /// </summary>
        /// <param name="existSecond"></param>
        /// <returns></returns>
        public T PickObjWithDelayRecyle(int existSecond, Action<T> Func = null)
        {
            var obj = PickObj();
            //startRecycleExplosion(obj.Key, existSecond);
            //var t= new System.Threading.Thread(new System.Threading.ParameterizedThreadStart((thread) =>
            //{
            //	System.Threading.Thread.Sleep(existSecond * 1000);
            //	RecyleObj(obj.Id,Func);
            //	((System.Threading.Thread)thread).Abort();
            //}));
            //t.Start(t);
            return obj;
        }

        /// <summary>
        /// 回收一个物体
        /// </summary>
        /// <param name="id"></param>
        public void RecyleObj(int id, Action<T> Func = null)
        {
            if (Func != null)
            {
                Func((T)this._totalObjList[id]);
            }
            //lock (asyncLockIdList)
            {
                if (!_avaliableIdList.Contains(id))
                    _avaliableIdList.Enqueue(id);
            }
        }


        private void NewObj()
        {
            // no allocated object is available
            T tObj = default(T);
            //CustomDebug.Log(typeof(BaseUnitPoolable).IsAssignableFrom(typeof(T)));
            if (!(typeof(UnityEngine.MonoBehaviour)).IsAssignableFrom(typeof(T)))
                tObj = new T();
            int start = _startCapacityIndex;
            //lock (asyncLockIdList)
            {
                _avaliableIdList.Enqueue(start);
            }

            if (m_onetimeInitFunc != null)
                tObj = m_onetimeInitFunc(tObj, this);

            //lock (asyncLockObjList)
            {
                _totalObjList.Add(start, tObj);
            }
            _startCapacityIndex++;

        }

        public void ResetAll()
        {
            //重置索引
            //m_nextAvailableIndex = 0;
        }
        public void Clear()
        {
            for (int i = 0; i < _totalObjList.Count; i++)
            {
                if (m_clearAction != null)
                {
                    T temp = _totalObjList[i];
                    m_clearAction(ref temp);
                }

                _totalObjList[i] = default(T);
            }
            _avaliableIdList.Clear();
            _avaliableIdList = null;
            _totalObjList.Clear();
            _totalObjList = null;
            m_resetAction = null;
            m_onetimeInitFunc = null;
        }

    }
}


